package com.peas.platform.module.icims.base.sys.component;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.peas.platform.common.core.configuration.FeignConfig;
import com.peas.platform.common.core.configuration.RequestContextUtil;
import com.peas.platform.common.core.configuration.RequestContextUtilEnum;
import com.peas.platform.common.core.interceptor.BaseInterceptor;
import com.peas.platform.common.core.property.ProjectProperties;
import com.peas.platform.common.core.utils.LogUtil;
import com.peas.platform.common.core.utils.SystemTool;
import com.peas.platform.module.icims.base.sys.dto.VisitDto;
import com.peas.platform.module.icims.base.sys.service.VisitService;
import io.swagger.annotations.ApiOperation;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import javax.annotation.Resource;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

@Component
@Slf4j
public class VisitInterceptor implements BaseInterceptor {

    @Resource
    private ApplicationContext ac;
    @Resource
    private SystemTool systemTool;
    @Resource
    private ProjectProperties projectProperties;
    @Resource
    private VisitService visitService;

    @SneakyThrows
    private void visit(String traceId,
                       String userId,
                       String url,
                       Class<?> c,
                       Method m,
                       Long duration,
                       HttpServletRequest req,
                       HttpServletResponse resp) {
        VisitDto dto = new VisitDto();
        dto.setSystem(projectProperties.getBusiness());
        dto.setSystemName(projectProperties.getBusinessName());
        dto.setService(projectProperties.getService());
        dto.setServiceName(projectProperties.getServiceName());
        ApiOperation ao = m.getAnnotation(ApiOperation.class);
        if (ao != null) {
            dto.setInterfaceDesc(ao.value());
        }
        dto.setInterfaceUrl(url);
        dto.setClazz(c.getName());
        dto.setMethod(m.getName());
        dto.setReqContentType(req.getContentType());
        dto.setReqProtocol(req.getProtocol());
        dto.setReqScheme(req.getScheme());
        dto.setReqPathInfo(req.getPathInfo());
        dto.setReqPathTranslated(req.getPathTranslated());
        dto.setReqRequestUri(req.getRequestURI());
        dto.setReqRequestUrl(req.getRequestURL().toString());
        dto.setReqContextPath(req.getContextPath());
        dto.setReqQueryString(req.getQueryString());
        dto.setReqMethod(req.getMethod());
        dto.setReqAuthType(req.getAuthType());
        dto.setReqServletPath(req.getServletPath());
        dto.setReqRequestedSessionId(req.getRequestedSessionId());
        dto.setReqRemoteUser(req.getRemoteUser());
        dto.setReqRemoteAddr(req.getRemoteAddr());
        dto.setReqRemoteHost(req.getRemoteHost());
        dto.setReqRemotePort(req.getRemotePort());
        dto.setReqServerName(req.getServerName());
        dto.setReqServerPort(req.getServerPort());
        dto.setReqLocalAddr(req.getLocalAddr());
        dto.setReqLocalName(req.getLocalName());
        dto.setReqLocalPort(req.getLocalPort());
        Map<String, String> cookie = new HashMap<>();
        Cookie[] cookies = req.getCookies();
        if (cookies != null) {
            for (Cookie coo : cookies) {
                cookie.put(coo.getName(), coo.getValue());
            }
        }
        dto.setReqCookies(JSONUtil.toJsonStr(cookie));
        dto.setReqParameterMap(JSONUtil.toJsonStr(req.getParameterMap()));
        dto.setReqTrailerFields(JSONUtil.toJsonStr(req.getTrailerFields()));
        //请求Headers
        Map<String, String> header1 = new HashMap<>();
        Enumeration<String> headers = req.getHeaderNames();
        while (headers != null && headers.hasMoreElements()) {
            String name = headers.nextElement();
            header1.put(name, req.getHeader(name));
        }
        dto.setReqHeaders(JSONUtil.toJsonStr(header1));
        dto.setRespStatus(resp.getStatus());
        dto.setRespCharacterEncoding(resp.getCharacterEncoding());
        //响应ContentType
        dto.setRespContentType(resp.getContentType());
        //响应Headers
        Map<String, String> header2 = new HashMap<>();
        if (CollUtil.isNotEmpty(header2)) {
            for (String name : resp.getHeaderNames()) {
                header2.put(name, resp.getHeader(name));
            }
        }
        dto.setRespHeader(JSONUtil.toJsonStr(header2));
        dto.setVisitRequestId(traceId);
        dto.setVisitTime(DateTime.now());
        dto.setVisitUser(Long.valueOf(userId));
        //访问耗时(毫秒)
        dto.setVisitDuration(duration);
        visitService.save(dto);
    }

    @Override
    public Integer sort() {
        return 10;
    }

    @Override
    public void preHandle(HttpServletRequest request, HttpServletResponse response) {
        final String url = request.getRequestURI().toLowerCase();
        if (StrUtil.startWith(url, "/api")) {
            request.setAttribute("start_timestamp_millis", System.currentTimeMillis());
            RequestContextUtil.getCurrentContext().put(RequestContextUtilEnum.init_timestamp_millis, String.valueOf(System.currentTimeMillis()));
        }
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response) {
        final String url = request.getRequestURI().toLowerCase();
        if (StrUtil.startWith(url, "/api")) {
            request.setAttribute("end_timestamp_millis", System.currentTimeMillis());
            RequestContextUtil.getCurrentContext().put(RequestContextUtilEnum.init_timestamp_millis, String.valueOf(System.currentTimeMillis()));
        }
    }

    @SneakyThrows
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response) {
        final String url = request.getRequestURI().toLowerCase();
        if (url.startsWith("/api") && request.getAttribute("start_timestamp_millis") != null && request.getAttribute("end_timestamp_millis") != null) {
            Long start_timestamp_millis = (Long) request.getAttribute("start_timestamp_millis");
            Long end_timestamp_millis = (Long) request.getAttribute("end_timestamp_millis");
            final long duration = end_timestamp_millis - start_timestamp_millis;
            LogUtil.log(this.getClass(), "afterCompletion", request.getRequestURI(), duration);
            HandlerMethod handlerMethod = null;
            Map<String, RequestMappingHandlerMapping> map = ac.getBeansOfType(RequestMappingHandlerMapping.class);
            if (CollUtil.isNotEmpty(map) && CollUtil.isNotEmpty(map.values())) {
                for (RequestMappingHandlerMapping mapping : map.values()) {
                    HandlerExecutionChain handlerChain = mapping.getHandler(request);
                    if (handlerChain != null) {
                        handlerMethod = (HandlerMethod) handlerChain.getHandler();
                    }
                }
            }
            if (handlerMethod != null) {
                final HttpServletRequest req = request;
                final HttpServletResponse resp = response;
                final HandlerMethod hm = handlerMethod;
                if (systemTool.special()) {
                    LogUtil.log(this.getClass(), "afterCompletion", "visit(" + FeignConfig.core + "):" + url, duration);
                } else {
                    new Thread(() -> {
                        final String traceId = RequestContextUtil.getCurrentRequestId();
                        final String userId = RequestContextUtil.getCurrentUserId(true);
                        visit(traceId, userId, url, hm.getBeanType(), hm.getMethod(), duration, req, resp);
                    }).start();
                }
            }
        }
    }
}
